package mya_dc.master_server.threads;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;

import mya_dc.master_server.database.ClientsHashTable;
import mya_dc.master_server.database.ServersHashTable;
import mya_dc.master_server.database.SlavesHashSet;
import mya_dc.shared_classes.ConnectInfo;
import mya_dc.shared_classes.Message;
import mya_dc.shared_classes.SharedDefinitions;
import mya_dc.shared_classes.Transmitter;
import mya_dc.shared_classes.Message.EMessageType;
import mya_dc.shared_classes.UserProject;

/**
 * Handles the registrations of a new compilation servers on the master server.
 *
 * @author      Adam Levi
 * <br>			MYA
 */
public class CompiServListenerThread extends MYA_ConnectionThread implements Runnable
{
	// this will manage all the communication with the connected
	// compilation server
	
	public CompiServListenerThread(
			SlavesHashSet slavesArray,
			ClientsHashTable clientsArray,
			ServersHashTable compilationServers,
			Socket socket) throws IOException
	{
		super(
				new Transmitter(
						socket,
						null,
						SharedDefinitions.SecsBetweenTwoKeepAliveMessages));
		m_SlavesArray = slavesArray;
		m_ClientsArray = clientsArray;
		m_CompilationServers = compilationServers;
		m_Socket = socket;
		m_ServerNumber = 0;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void run()
	{
		if (!SharedDefinitions.isExpectedEntity(
				m_Transmitter,
				SharedDefinitions.Entity.ENTITY_COMPILATION,
				true)
			)
			return;
		while(true)
		{
			try
			{
				Message newMsg = m_Transmitter.receive();
				switch(newMsg.getType())
				{
					case LOGIN_NEW:
					{
						loginNewServer();
						break;
					}
					case LOGIN_EXISTING:
					{
						Object[] objArr = (Object[])newMsg.getObject();
						m_ServerNumber = ((Long)(objArr[0]));
						loginExistingServer((Integer)(objArr[1]));
						break;
					}
					case COMPILATION_GET_SLAVES:
					{
						getSlaves();
						break;
					}
					case SERVER_SYNCHRONIZE_DATABASE:
					{
						ArrayList<UserProject> m_ConnectedUsersArray = (ArrayList<UserProject>)newMsg.getObject();
						doSync(m_ConnectedUsersArray);
						break;
					}
					case SERVER_SYNCHRONIZE_DELETE_USERPROJECT:
					{
						UserProject current  = (UserProject)newMsg.getObject();
						if (m_ServerNumber > 0)
							m_ClientsArray.deleteMapping(current, m_ServerNumber, m_CompilationServers);
						break;
					}
					default: //do nothing
				}
			}
			catch (Exception e)
			{
				break;	
			}
		}
		m_Transmitter.close();
		//unregistering server from the list
		if (m_ServerNumber > 0)
			m_CompilationServers.unregisterServer(m_ServerNumber);		
	}
	
	/**
	 * should:
	 * <br>(1) Delete all mapping of Users+Projects to the compilation server that aren't on connectedUsersArray
	 * <br>(2) Send back (SERVER_SYNCHRONIZE_OK + null) message if all users exist on master server 
	 *      or (SERVER_SYNCHRONIZE_DELETE_USERS + String ArrayList of Users that don't exist anymore and couldn't be added) message.
	 * 
	 * @param connectedUsersArray
	 */
	private void doSync(ArrayList<UserProject> connectedUsersArray)
	{	
		if (connectedUsersArray == null)
			return;
		
		ArrayList<UserProject> deUserProject = new ArrayList<UserProject>();
		for(UserProject currect : connectedUsersArray)
		{
			if (!m_ClientsArray.mappingExists(currect, m_ServerNumber, m_CompilationServers))
				deUserProject.add(currect);
		}
		
		m_ClientsArray.syncMappings(connectedUsersArray, m_ServerNumber, m_CompilationServers);
		
		try {
			if (deUserProject.isEmpty())
			{
				Message tmpMessage = new Message(EMessageType.SERVER_SYNCHRONIZE_OK,null);
				m_Transmitter.send(tmpMessage);
			}
			else
			{
				Message tmpMessage = new Message(EMessageType.SERVER_SYNCHRONIZE_DELETE_USERS,deUserProject);
				m_Transmitter.send(tmpMessage);
			}
		}
		catch (Exception e)
		{
			return;
		}
	}

	private void loginNewServer() throws IOException
	{
		m_ServerNumber = m_CompilationServers.registerServer(m_Socket.getInetAddress());
		if (m_ServerNumber == 0) //error
		{
			throw new IOException();
		}
		Message tmpMessage = new Message(EMessageType.SUCCESS,m_ServerNumber);
		m_Transmitter.send(tmpMessage);
	}
	
	private void loginExistingServer(int port) throws IOException
	{
		InetAddress address = m_Socket.getInetAddress(); // to avoid entering null values
		if (address.getHostName().equals("localhost") || address.getHostName().equals("127.0.0.1"))
		{
			address = InetAddress.getLocalHost();
		}
		if (address == null)
			throw new IOException(); //connection error
		if (m_CompilationServers.registerServer(
				new ConnectInfo(address.getHostAddress(),port), m_ServerNumber) == -1)
		{
			m_Transmitter.send(new Message(EMessageType.LOGIN_FAILED_ID_ALREADY_IN_USE,null));	
		}
		else
		{
			m_Transmitter.send(new Message(EMessageType.SUCCESS,null));
		}
	}
	
	private void getSlaves() throws IOException
	{
		Object[] slavesArr = m_SlavesArray.toArray();
		Message slaveArrMsg = new Message(EMessageType.SUCCESS,slavesArr);
		m_Transmitter.send(slaveArrMsg);
	}
	
	private ClientsHashTable 				m_ClientsArray;
 	private java.util.HashSet<ConnectInfo>	m_SlavesArray;
	private ServersHashTable 				m_CompilationServers;
	private Socket 							m_Socket;
	private long 							m_ServerNumber;
}
